package echo.netty.testByteBuf;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;

import static io.netty.buffer.ByteBufUtil.appendPrettyHexDump;
import static io.netty.util.internal.StringUtil.NEWLINE;

/**
 * @author： zhangnan
 * @date： 2025/5/7 18:00
 * @description： TODO
 * @modifiedBy：
 * @version: 1.0
 */
public class TestByteBuf {
    public static void main(String[] args) {

        /**
         *  ByteBuf 优势
         *         ◆ 池化-可以重用池中ByteBuf实例，更节约内存，减少内存溢出的可能;
         *         ◆ 读写指针分离，不需要像ByteBuffer一样切换读写模式;
         *         ◆ 可以自动扩容;
         *         ◆ 支持链式调用，便用更流畅;
         *         ◆ 很多地方体现零拷贝，例如 slice、duplicate、CompositeByteBuf
         */

        /**
         * 池化 vs 非池化
         *  池化可以重用ByteBuf，有点如下：
         *   1. 没有池化，则每次都得创建新的ByteBuf实例，这个操作对直接内存代价昂贵，就算堆内存，也会增加GC压力
         *   2. 有了池化，则可以重用池中ByteBuf实例，并且采用了与jemalloc类似的内存分配算法提升分配效率
         *   3. 高并发时，池化功能更节约内存，减少内存溢出的可能
         *   4. 池化功能默认是开启的，设置是否开启可以设置系统环境变量（unpooled 是关闭池化功能） -Dio.netty.allocator.type={unpooled|pooled}
         *   5. 4.1之前不太成熟，建议在4.1以后使用
         *
         * ByteBuf 组成
         *  读指针、写指针、容量、最大容量
         *  容量和最大容量之间是可扩容部分
         *  写指针和容量之间的部分即为：可写部分
         *  读指针和写指针之间的部分即为：可读部分
         *  读指针读过的部分即为：废弃部分
         *
         * 对比nio中的ByteBuffer
         *  1. 因为有读写指针，所以netty的ByteBuf无需向nio那样在读写时还需要调用切换方法
         *  2. netty的ByteBuf可以自动扩容
         *
         * 写入
         *  以write开头的一系列方法，
         *
         *  扩容规则
         *  如果写入后数据大小未超过 512，则选择一个 16 的整数倍，例如：写入后大小为 12，则扩容后capacity(容量)是 16
         *  如果写入后数据大小超过 512，则选择下一个 2^n，例如：写入后大小为 513，则扩容后capacity(容量)是 2^n=1024(2^9=512已经不够了)
         *  扩容不能超过 max capacity(最大容量，默认是整数的最大值20亿)，否则会报错
         *
         * 读取
         *  以read或get开头的方法，get开头方法不会改变读指针的位置
         *  想要重复读取可以使用：buffer.markReaderIndex(); 在read前先做个标记，读完后可以调用buffer.resetReaderIndex();重置到标记位置，重复读取已经读过的数据
         */

        // 读指针：ridx；写指针：widx；容量：cap
        // 堆内容的 分配效率较高，但是读写效率低
        ByteBuf buf1 = ByteBufAllocator.DEFAULT.heapBuffer(10);// 堆内存
        System.out.println(buf1.getClass());  // 类名带有Pooled字样就是池化的
        // 直接内存 的 分配效率较低，但是读写效率高
        ByteBuf buf2 = ByteBufAllocator.DEFAULT.directBuffer(10);// 直接内存
        ByteBuf defaultBuf = ByteBufAllocator.DEFAULT.buffer(); // 默认使用 直接内存
        log(defaultBuf); // 查看容量
        // 测试ByteBuf自动扩容
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 32; i++) {
            sb.append("a");
        }
        defaultBuf.writeBytes(sb.toString().getBytes()); // 向ByteBuf中写入数据
        log(defaultBuf); // buf从原始容量是256 自动扩容到了 512
    }

    private static void log(ByteBuf buffer) {
        int length = buffer.readableBytes();
        int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;
        StringBuilder buf = new StringBuilder(rows * 80 * 2)
                .append("read index:").append(buffer.readerIndex())
                .append(" write index:").append(buffer.writerIndex())
                .append(" capacity:").append(buffer.capacity())
                .append(NEWLINE);
        appendPrettyHexDump(buf, buffer);
        System.out.println(buf.toString());
    }
}
